﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web;
using HtmlAgilityPack;

namespace HttpCrawler.Engine
{
    public class HtmlHttpCrawlerContentProcessor : IHttpCrawlerContentProcessor
    {
        public bool CanProcess(HttpCrawlerContent content)
        {
            return content is HtmlHttpCrawlerContent;
        }

        public IEnumerable<string> Process(HttpCrawlerContent content)
        {
            var htmlContent = (HtmlHttpCrawlerContent)content;
            var doc = htmlContent.Html;

            doc.LoadHtml(EscapeIEConditionalComments(htmlContent.Text));

            var foundUrls = new HashSet<string>();

            CollectUrls(foundUrls, doc, "a", "href");
            CollectUrls(foundUrls, doc, "area", "href");
            CollectUrls(foundUrls, doc, "frame", "src");
            CollectUrls(foundUrls, doc, "iframe", "src");
            CollectUrls(foundUrls, doc, "img", "src");
            CollectUrls(foundUrls, doc, "input", "src");
            CollectUrls(foundUrls, doc, "link", "href");
            CollectUrls(foundUrls, doc, "object", "data");
            CollectUrls(foundUrls, doc, "script", "src");

            doc.LoadHtml(UnescapeIEConditionalComments(htmlContent.Text));

            return foundUrls;
        }

        #region IE Conditional Support

        static Regex _IEConditionalStartRegex = new Regex(@"(<!--\[if(?:.*?)IE(?:.*?)\])(>)", RegexOptions.Compiled | RegexOptions.Singleline);

        static Regex _IEConditionalEndRegex = new Regex(@"(<!)(\[endif\]-->)", RegexOptions.Compiled | RegexOptions.Singleline);

        const string _IEConditionalReplaceString = "$1--$2";

        static Regex _EscapedIEConditionalStartRegex = new Regex(@"(<!--\[if(?:.*?)IE(?:.*?)\])(--)(>)", RegexOptions.Compiled | RegexOptions.Singleline);

        static Regex _EscapedIEConditionalEndRegex = new Regex(@"(<!)(--)(\[endif\]-->)", RegexOptions.Compiled | RegexOptions.Singleline);

        const string _EscapedIEConditionalReplaceString = "$1$3";

        protected static string EscapeIEConditionalComments(string input)
        {
            var startingTagsEscaped = _IEConditionalStartRegex.Replace(input, _IEConditionalReplaceString);

            return _IEConditionalEndRegex.Replace(startingTagsEscaped, _IEConditionalReplaceString);
        }

        protected static string UnescapeIEConditionalComments(string input)
        {
            var startingTagsUnescaped = _EscapedIEConditionalStartRegex.Replace(input, _EscapedIEConditionalReplaceString);

            return _EscapedIEConditionalEndRegex.Replace(startingTagsUnescaped, _EscapedIEConditionalReplaceString);
        }

        #endregion

        private static void CollectUrls(HashSet<string> links, HtmlDocument htmlDocument, string elementName, string attributeName)
        {
            var elements = htmlDocument.DocumentNode.SelectNodes("//" + elementName + "[@" + attributeName + "]");

            if (elements == null)
            {
                return;
            }

            var urls = elements.Select(e => e.Attributes[attributeName]).Select(a => HttpUtility.HtmlDecode(a.Value));

            links.AddRange(urls);
        }
    }
}
